#pragma once
#include <stdio.h>

typedef enum {
  TOK_INTEGER, 
  TOK_COLON, 
  TOK_STRING, 
  TOK_WORD,
  TOK_LBRACKET,
  TOK_RBRACKET,
  TOK_EOS
} TOK;

typedef struct NString {
  int size;
  char* data;
} NString;

typedef union {
  int integer;
  NString string;
} TokenValue;

typedef struct Token {
  TOK type;
  TokenValue v;

  /* for error detect */
  int line;
  char* context;
} Token;

typedef struct Lexer {
  int line;
  Token token;
  Token next;
  char* base;
  char* p;
} Lexer;

typedef enum {
  WT_BUILTIN,
  WT_NORMAL
} WORD_TYPE;

struct SikleState;

typedef void (*builtin_word_func)(struct SikleState* ss);

typedef struct Word {
  WORD_TYPE type;
  NString name;
  union {
    builtin_word_func func;
    unsigned int entry;
  } u;
} Word;

typedef struct StringPool {
  char* base;
  unsigned int size;
  unsigned int end;
} StringPool;

typedef struct GramelPool {
  void* base;
  unsigned int size;
  unsigned int pc;
} GramelPool;

typedef struct ExecStack {
  void* base;
  unsigned int size;
  unsigned int sp;
} ExecStack;

typedef struct SPString {
  int size;
  unsigned int offset;
} SPString;

typedef enum GRAMEL_TYPE {
  GT_INTEGER,
  GT_STRING,
  GT_BUILTIN_WORD,
  GT_WORD,
  GT_ANONYMOUS_WORD,
  GT_END
} GRAMEL_TYPE;

typedef struct GInteger {
  GRAMEL_TYPE type;
  int value;
} GInteger;

typedef struct GString {
  GRAMEL_TYPE type;
  SPString sps;
} GString;

typedef struct GWord {
  GRAMEL_TYPE type;
  unsigned int entry;
} GWord;

typedef struct GAnonymousWord {
  GRAMEL_TYPE type;
  unsigned int entry;
} GAnonymousWord;

typedef struct GEnd {
  GRAMEL_TYPE type;
} GEnd;

typedef struct GBuiltinWord {
  GRAMEL_TYPE type;
  SPString name;
} GBuiltinWord;

typedef struct Gramel {
  GRAMEL_TYPE type;
} Gramel;

typedef struct Dictionary Dictionary;

typedef struct SikleState {
  GramelPool* word_pool;
  GramelPool* gramel_pool;
  StringPool* string_pool;
  StringPool* string_pool_tmp;
  Dictionary* dictionary;
  ExecStack* exec_stack;
} SikleState;

#define GRAMEL(pg) ((Gramel*)(pg))
#define GWORD(gramel) ((GWord*)(gramel))
#define GANONYMOUSWORD(gramel) ((GAnonymousWord*)(gramel))
#define GBUILTINWORD(gramel) ((GBuiltinWord*)(gramel))
#define GINTEGER(gramel) ((GInteger*)(gramel))
#define GSTRING(gramel) ((GString*)(gramel))
#define GEND(gramel) ((GEnd*)(gramel))

/* log.c */
typedef enum LOG_LEVEL {
  LOG_MESSAGE, LOG_WARNING, LOG_ERROR, LOG_FATAL
} LOG_LEVEL;

void error_word_not_defined(Token* tok);
void log_msg(LOG_LEVEL level, const char* msg, const Token* tok);

/* dictionary.c */
#define HASH_BUCKET_SIZE 17
Dictionary* Dictionary_new();
void Dictionary_delete(Dictionary* dict);
void Dictionary_put_word(Dictionary* dict, Word word);
Word* Dictionary_get_word(Dictionary* dict, NString name);

#define TRUE  1
#define FALSE 0
/* lex.c */
#define tok_type(lex) ((lex)->token.type)
#define tok_integer(lex) ((lex)->token.v.integer)
#define tok_string(lex) ((lex)->token.v.string)

void next_token(Lexer* lex);
void peek(Lexer* lex);
void Lexer_init(Lexer* lexer, char* filename);

/* nstring.c */
NString NString_new(char* str, int size);
void NString_delete(NString ns);
void NString_set(NString* ns, char* str, int size);
int NString_equal(NString ns, NString nd);
unsigned int NString_hash(NString ns);

/* gramelpool.c */
GramelPool* GramelPool_new(unsigned int size);
void GramelPool_delete(GramelPool* gp);
void GramelPool_put(GramelPool* gp, Gramel* gramel);
void GramelPool_dump(GramelPool* gp, StringPool* sp);

/* stringpool.c */
StringPool* StringPool_new(unsigned int size);
void StringPool_delete(StringPool* sp);
SPString SPString_new(StringPool* pool, char* data, int size);
SPString SPString_concat(StringPool* pool, SPString sps0, SPString sps1);
void StringPool_dump(StringPool* sp);

/* translate.c */
void translate_main(SikleState* ss, Lexer* lex);

/* execstack.c */
typedef struct StacelOperation StacelOperation;

#define STACEL_TYPES					\
  X(STACEL_INTEGER, integer_ops)			\
  X(STACEL_STRING, string_ops)				\
  X(STACEL_ANONYMOUS_WORD, anonymous_word_ops)		\
  X(STACEL_POINTER, pointer_ops)			\
  X(STACEL_FILE, file_ops)

#define X(a, b) a,
typedef enum { STACEL_TYPES STACEL_TYPE_END } STACEL_TYPE;
#undef X

#define X(a, b) extern StacelOperation b;
STACEL_TYPES
#undef X

typedef struct StacelType {
  char* name;
  StacelOperation* operation;
} StacelType;

typedef struct Stacel {
  STACEL_TYPE type;
} Stacel;

struct StacelOperation {
  /* when we push a stacel, we should leave a STACEL_TYPE on the top of ExecStack */
  void (*push)(SikleState* ss, Stacel* stacel);
  void (*pop)(SikleState* ss, Stacel* stacel);
  void (*drop)(SikleState* ss);
  void (*dup)(SikleState* ss);

  void (*clone)(SikleState* ss);
  void (*register_builtin_words)(SikleState* ss);

  int (*to_integer)(SikleState* ss);
  SPString (*to_string)(SikleState* ss);
  int (*to_boolean)(SikleState* ss);

  void (*p)(SikleState* ss);

  void (*add)(SikleState* ss);
  void (*sub)(SikleState* ss);
  void (*mul)(SikleState* ss);
  void (*div)(SikleState* ss);

  void (*gt)(SikleState* ss);
  void (*ngt)(SikleState* ss);
  void (*lt)(SikleState* ss);
  void (*nlt)(SikleState* ss);
  void (*equ)(SikleState* ss);
};

extern StacelType stacel_types[];
#define STACEL(p) ((Stacel*)(p))

#define operation_with_check(type, op, ss)			\
  do {								\
    if(type.operation->op != NULL)				\
      type.operation->op(ss);					\
    else {							\
      printf("ERROR: '"#op"' not support for %s\n", type.name);	\
    }								\
  }while(0)

#define operation_with_check_cond(type, op, cond, ss)		\
  do {								\
    if(type.operation->op != NULL)				\
      cond = type.operation->op(ss);				\
    else {							\
      printf("ERROR: '"#op"' not support for %s\n", type.name);	\
    }								\
  }while(0)

#define get_stacel_type(ss) \
  (STACEL((ss)->exec_stack->base + (ss)->exec_stack->sp - sizeof(Stacel))->type)

#define get_stacel(ss) \
  (STACEL((ss)->exec_stack->base + (ss)->exec_stack->sp - sizeof(Stacel)))

/* Stacel Integer Type */
typedef struct SInteger {
  STACEL_TYPE type;
  int value;
} SInteger;

typedef struct SString {
  STACEL_TYPE type;
  SPString value;
} SString;

typedef struct SAnonymousWord {
  STACEL_TYPE type;
  unsigned int entry;
} SAnonymousWord;

typedef struct SPointer {
  STACEL_TYPE type;
  void* value;
} SPointer;

typedef struct SFile {
  STACEL_TYPE type; 
  FILE* value;
} SFile;

typedef struct SContainerStruct {
  union {
    SPointer pointer;
    SInteger integer;
    SString string;
    SFile file;
    SAnonymousWord word;
  };
} SContainerStruct;

void type_check(STACEL_TYPE type, STACEL_TYPE type_expected);
void exec_gramel(SikleState* ss, unsigned int pc, int in_word_pool);
void exec_gramel_main(SikleState* ss);
ExecStack* ExecStack_new(unsigned int size);

/* builtin.c */
void register_builtin_words(SikleState* ss);
void register_builtin_word(SikleState* ss, char* name, builtin_word_func bwf);

#define MIN(x, y) ((x) < (y) ? (x) : (y))


/* library */
#define SIKLE_LIBS					\
  X(SIKLE_STDLIB, stdlib_ops)			


#define X(a, b) a,
typedef enum { SIKLE_LIBS SIKLE_LIB_END } SIKLE_LIB;
#undef X

typedef void (*library_init_func)(SikleState* ss);
typedef void (*register_library_words_func)(SikleState* ss);
typedef void (*library_destory_func)(SikleState* ss);

typedef struct SikleLibOperation {
  library_init_func library_init;
  register_library_words_func register_library_words;
  library_destory_func library_destory;
} SikleLibOperation;

#define X(a, b) extern SikleLibOperation b;
SIKLE_LIBS
#undef X

typedef struct SikleLib {
  char* name;
  SikleLibOperation* operation;
} SikleLib;

void register_librarys(SikleState* ss);
